package com.example.wac.service.impl;

import cn.hutool.core.util.PhoneUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.example.wac.constant.Constant;
import com.example.wac.enums.ErrorCode;
import com.example.wac.service.SmsService;
import com.example.wac.util.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author wuancheng
 * @description 发送短信服务
 * @date 2022-04-21 14:11:00
 */

@Service
@Slf4j
public class SmsServiceImpl implements SmsService {

    @Autowired
    RedisTemplate redisTemplate;

    @Override
    public boolean sendSms(String phoneNumbers, String templateCode, Map<String, Object> code, HttpServletRequest request) {
        if (!PhoneUtil.isMobile(phoneNumbers)){
            throw ErrorCode.PHONE_NUMBER_IS_ILLEGAL.getAppException();
        }
        // 同一手机号次数校验
        checkMobileSms(phoneNumbers);
        // 同一Ip次数校验
        checkIpSms(request);
        // 发送短信
        boolean send = send(phoneNumbers, templateCode, code);
        if (send){
            // 发送成功次数+1
            redisTemplate.opsForValue().set(String.format(Constant.RedisKey.MOBILE_LIMIT_COUNT_KEY, phoneNumbers), 1, 1, TimeUnit.MINUTES);
            redisTemplate.opsForValue().increment(String.format(Constant.RedisKey.MOBILE_DAY_LIMIT_COUNT_KEY, phoneNumbers), 1);
            redisTemplate.opsForValue().increment(String.format(Constant.RedisKey.MOBILE_DAY_LIMIT_COUNT_KEY, IpUtil.getIpAddress(request)), 1);
            return true;
        }else {
            return false;
        }
    }

    /**
     * SDK是代码参考实例 API是接口调用参数返回
     * 参考文档https://help.aliyun.com/document_detail/112148.html
     * @param phoneNum
     * @param templateCode
     * @param code
     * @return
     */
    private boolean send(String phoneNum, String templateCode, Map<String, Object> code) {
        // 连接阿里云
        DefaultProfile profile = DefaultProfile.getProfile(Constant.Sms.regionId, Constant.Sms.accessKeyId, Constant.Sms.secret);
        IAcsClient client = new DefaultAcsClient(profile);

        SendSmsRequest request = new SendSmsRequest();
        // 接收短信的手机号码
        request.setPhoneNumbers(phoneNum);
        // 短信签名名称
        request.setSignName(Constant.Sms.signName);
        // 短信模板CODE
        request.setTemplateCode(templateCode);
        // 短信模板变量对应的实际值
        request.setTemplateParam(JSONObject.toJSONString(code));
        try {
            SendSmsResponse response = client.getAcsResponse(request);
            return "OK".equals(response.getCode());
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (ClientException e) {
            log.error("ErrCode:" + e.getErrCode());
            log.error("ErrMsg:" + e.getErrMsg());
            log.error("RequestId:" + e.getRequestId());
        }
        return false;
    }

    /**
     * 同一手机号次数校验
     * 一分钟内只能发送1次
     * 一天最多发送5次
     */
    private void checkMobileSms(String phoneNumbers){
        // 一分钟内只能发送1次
        Integer count = (Integer) redisTemplate.opsForValue().get(String.format(Constant.RedisKey.MOBILE_LIMIT_COUNT_KEY, phoneNumbers));
        if(count != null){
            throw ErrorCode.EXCEED_SMS_COUNT.getAppException();
        }

        // 一天最多发送5次
        Integer dayCount = (Integer) redisTemplate.opsForValue().get(String.format(Constant.RedisKey.MOBILE_DAY_LIMIT_COUNT_KEY, phoneNumbers));
        if (dayCount == null){
            redisTemplate.opsForValue().set(String.format(Constant.RedisKey.MOBILE_DAY_LIMIT_COUNT_KEY, phoneNumbers), 0, 1, TimeUnit.DAYS);
        }else {
            if (dayCount >= Constant.Sms.SMS_MOBILE_COUNT){
                throw ErrorCode.EXCEED_SMS_COUNT.getAppException(Constant.Sms.SMS_MOBILE_COUNT);
            }
        }
    }

    /**
     * 同一Ip次数校验
     */
    private void checkIpSms(HttpServletRequest request){
        String ipAddress = IpUtil.getIpAddress(request);
        // 一天同一IP最多发送10次
        Integer dayCount = (Integer) redisTemplate.opsForValue().get(String.format(Constant.RedisKey.MOBILE_DAY_LIMIT_COUNT_KEY, ipAddress));
        if (dayCount == null){
            redisTemplate.opsForValue().set(String.format(Constant.RedisKey.MOBILE_DAY_LIMIT_COUNT_KEY, ipAddress), 0, 1, TimeUnit.DAYS);
        }else {
            if (dayCount >= Constant.Sms.SMS_IP_COUNT) {
                throw ErrorCode.EXCEED_SMS_COUNT.getAppException(Constant.Sms.SMS_IP_COUNT);
            }
        }
    }
}
